package com.tfd.blog.controller;

import com.tfd.blog.domain.Article;
import com.tfd.blog.domain.Category;
import com.tfd.blog.exception.BlogExceptionCode;
import com.tfd.blog.filter.NeedLogin;
import com.tfd.blog.repository.ArticleRepository;
import com.tfd.blog.repository.CategoryRepository;
import com.tfd.blog.service.ArticleService;
import com.tfd.blog.service.LabelService;
import com.tfd.blog.utlis.BlogConstants;
import com.tfd.blog.utlis.JsonResult;
import com.tfd.blog.utlis.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author TangFD
 * @since 2019/5/7.
 */
@Slf4j
@RestController
@RequestMapping("/article")
public class ArticleController {

    @Resource
    private ArticleService articleService;
    @Resource
    private ArticleRepository articleRepository;
    @Resource
    private CategoryRepository categoryRepository;
    @Resource
    private LabelService labelService;

    @NeedLogin
    @RequestMapping("/manage/save")
    public JsonResult save(@RequestBody Article article) {
        JsonResult result = new JsonResult();
        if (article == null) {
            result.setMessage(BlogExceptionCode.ILLEGAL_ARGUMENT.name(), false);
            return result;
        }

        try {
            articleService.save(article);
            result.setData(article.getId());
            return result;
        } catch (Exception e) {
            log.error("save article exception[" + article + "]", e);
            result.setMessage(BlogExceptionCode.SYSTEM_ERROR.name(), false);
            return result;
        }
    }

    @RequestMapping("/load/{id}")
    public JsonResult<Article> load(@PathVariable("id") String id) {
        if (StringUtils.isEmpty(id)) {
            return new JsonResult<>(BlogExceptionCode.ILLEGAL_ARGUMENT.name(), false);
        }

        Article article = articleRepository.findById(id).get();
        if (article == null) {
            return new JsonResult<>(article);
        }

        setCategoryAndLabels(Collections.singleton(article));
        if (article.getPublishDate() == null) {
            return new JsonResult<>(article);
        }

        Article pre = articleService.getPreArticleId(article.getPublishDate());
        Article next = articleService.getNextArticleId(article.getPublishDate());
        article.setPre(pre);
        article.setNext(next);
        return new JsonResult<>(article);
    }

    @NeedLogin
    @RequestMapping("/page")
    public JsonResult<Page> page(String keyword, @RequestBody Page<Article> page) {
        if (page == null) {
            return new JsonResult<>(BlogExceptionCode.ILLEGAL_ARGUMENT.name(), false);
        }

        Sort sort = Sort.by(Sort.Order.desc("updateDate"));
        PageRequest pageable = PageRequest.of(page.getPageNum() - 1, page.getPageSize(), sort);
        org.springframework.data.domain.Page<Article> articlePage;
        if (StringUtils.isEmpty(keyword)) {
            articlePage = articleRepository.findAll(pageable);
        } else {
            keyword = "%" + keyword + "%";
            articlePage = articleRepository.findByTitleLikeOrContentLikeOrDescriptionLikeOrderByCreateDateDesc(
                    keyword, keyword, keyword, pageable);
        }

        page.setTotal(articlePage.getTotalElements());
        List<Article> articleList = articlePage.getContent();
        if (CollectionUtils.isEmpty(articleList)) {
            return new JsonResult<>(page);
        }

        setCategoryAndLabels(articleList);
        page.setList(articleList);
        return new JsonResult<>(page);
    }

    private void setCategoryAndLabels(Collection<Article> articleList) {
        Set<String> categoryIds = new HashSet<>(articleList.size());
        List<String> articleIds = new ArrayList<>(articleList.size());
        articleList.forEach(article -> {
            categoryIds.add(article.getCategoryId());
            articleIds.add(article.getId());
        });

        Map<String, String> categoryIdAndNameMap = getCategoryIdAndNameMap(categoryIds);
        Map<String, List<String>> articleIdAndLabelNamesMap = labelService.getArticleIdAndLabelNamesMap(articleIds);
        if (CollectionUtils.isEmpty(categoryIdAndNameMap) && CollectionUtils.isEmpty(articleIdAndLabelNamesMap)) {
            return;
        }

        articleList.forEach(article -> {
            String categoryId = article.getCategoryId();
            String categoryName = categoryIdAndNameMap.get(categoryId);
            if (!StringUtils.isEmpty(categoryName)) {
                article.setCategoryName(categoryName);
            }
            String id = article.getId();
            List<String> labelNames = articleIdAndLabelNamesMap.get(id);
            if (!CollectionUtils.isEmpty(labelNames)) {
                article.setLabels(labelNames);
            }
        });
    }

    private Map<String, String> getCategoryIdAndNameMap(Set<String> categoryIds) {
        List<Category> categories = categoryRepository.findByIdIn(categoryIds);
        if (CollectionUtils.isEmpty(categories)) {
            return new HashMap<>(0);
        }

        return categories.stream().collect(Collectors.toMap(Category::getId,
                Category::getName, (a, b) -> b, () -> new HashMap<>(categories.size())));

    }

    @RequestMapping("/publishPage")
    public JsonResult<Page> publishPage(String categoryId, Page<Article> page) {
        if (page == null) {
            page = new Page<>();
        }

        List<String> categoryIds;
        if (StringUtils.isEmpty(categoryId)) {
            categoryIds = categoryRepository.findAllIds();
        } else {
            categoryIds = Collections.singletonList(categoryId);
        }

        PageRequest pageable = PageRequest.of(page.getPageNum() - 1, page.getPageSize());
        org.springframework.data.domain.Page<Article> articlePage =
                articleRepository.findByStatusAndCategoryIdInOrderByUpdateDateDesc(
                        BlogConstants.BLOG_STATUS_PUBLISH, categoryIds, pageable);
        page.setTotal(articlePage.getTotalElements());
        List<Article> articleList = articlePage.getContent();
        if (CollectionUtils.isEmpty(articleList)) {
            return new JsonResult<>(page);
        }

        setCategoryAndLabels(articleList);
        page.setList(articleList);
        return new JsonResult<>(page);
    }

    @NeedLogin
    @PutMapping("/manage/enable/{id}")
    public JsonResult enable(@PathVariable("id") String id) {
        try {
            articleService.updateStatus(id, BlogConstants.BLOG_STATUS_PUBLISH);
            return new JsonResult();
        } catch (Exception e) {
            return new JsonResult(null, false);
        }
    }

    @NeedLogin
    @PutMapping("/manage/disabled/{id}")
    public JsonResult disabled(@PathVariable("id") String id) {
        try {
            articleService.updateStatus(id, BlogConstants.BLOG_STATUS_DISABLED);
            return new JsonResult();
        } catch (Exception e) {
            return new JsonResult(null, false);
        }
    }

    @NeedLogin
    @PutMapping("/manage/recommend/{id}")
    public JsonResult recommend(@PathVariable("id") String id) {
        try {
            Article article = articleRepository.findById(id).get();
            if (StringUtils.isEmpty(article.getId())) {
                return new JsonResult(null, false);
            }

            article.setRecommend(!article.isRecommend());
            articleService.simpleSave(article);
            return new JsonResult();
        } catch (Exception e) {
            return new JsonResult(null, false);
        }
    }

    @NeedLogin
    @DeleteMapping("/manage/delete/{id}")
    public JsonResult delete(@PathVariable("id") String id) {
        try {
            articleRepository.deleteById(id);
            return new JsonResult();
        } catch (Exception e) {
            return new JsonResult(null, false);
        }
    }

    @GetMapping("/praise/{id}")
    public JsonResult praise(@PathVariable("id") String id) {
        try {
            articleRepository.praise(id);
            return new JsonResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(null, false);
        }
    }

    @GetMapping("/read/{id}")
    public JsonResult read(@PathVariable("id") String id) {
        try {
            articleRepository.read(id);
            return new JsonResult();
        } catch (Exception e) {
            return new JsonResult(null, false);
        }
    }

    @GetMapping("/categoryCount")
    public JsonResult<List<Map<String, Object>>> categoryCount() {
        try {
            return new JsonResult<>(articleRepository.categoryCount());
        } catch (Exception e) {
            return new JsonResult<>(null, false);
        }
    }
}
